﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;
using System.Text;
using System.Timers;
using System.Diagnostics;

namespace PerfTabControl
{
    class ChartTabPage : TabPage
    {
        #region Member Fields
        private int _SelectedIndex = -1;
        private System.Timers.Timer _timer;
        protected delegate void AddDataDelegate();
        protected AddDataDelegate addDataDel;
        #endregion
        #region Constructor
        /// <summary> 
        /// The default constructor 
        /// </summary> 
        public ChartTabPage()
            :base()
        {
        }
        public ChartTabPage(string Name)
            : base(Name)
        {

        }
        public ChartTabPage(bool AutoSelectNewPerfChart)
            : base()
        {
            this.AutoSelectNewPerfChart = AutoSelectNewPerfChart;
        }
        public ChartTabPage(string Name, bool AutoSelectNewPerfChart)
            : base(Name)
        {
            this.AutoSelectNewPerfChart = AutoSelectNewPerfChart;
        }

        #endregion
        #region ---PrivateMethods---
        protected override void OnCreateControl()
        {
            base.OnCreateControl();
            //create and define default chart
            ChartTab = new Chart { Dock = DockStyle.Fill,BackColor = System.Drawing.Color.Transparent };
            this.Controls.Add(ChartTab);

            PerfChart newchart = new PerfChart();
            ChartTab.ChartAreas.Add(newchart);
            ChartTab.MouseClick += new MouseEventHandler(ChartTab_MouseClick);
            SelectedPerfChart = newchart; //todo: might be broken

            //prep timer
            _timer = new System.Timers.Timer(1000);
            _timer.Elapsed += new ElapsedEventHandler(AddDataThreadLoop);
            addDataDel += new AddDataDelegate(AddData);
        }
        protected override void Dispose(bool disposing)
        {
            if (_timer.Enabled == true)
            {
                _timer.Stop();
            }
            base.Dispose(disposing);
        }
        private void AddDataThreadLoop(object sender, ElapsedEventArgs e)
        {
            ChartTab.Invoke(addDataDel);
        }
        private void AddData()
        {
            DateTime timestamp = DateTime.Now;
            foreach (Counter ctr in ChartTab.Series)
            {
                //update series
                
                float val = ctr.PerfCounter.NextValue();
                ctr.Points.AddXY(timestamp.ToOADate(), val);
                double removeBefore = timestamp.AddSeconds((double)(110) * (-1)).ToOADate();
                while (ctr.Points[0].XValue < removeBefore)
                {
                    ctr.Points.RemoveAt(0);
                }

                //Add point
                PerfChart Chart = (PerfChart)ChartTab.ChartAreas[ctr.ChartArea];
                Chart.AxisX.Minimum = ctr.Points[0].XValue;
                Chart.AxisY.Maximum = double.NaN;
                Chart.AxisX.Maximum = DateTime.FromOADate(ctr.Points[0].XValue).AddMinutes(2).ToOADate();

                //update chart
                ChartTab.Invalidate();


            }
        }
        void ChartTab_MouseClick(object sender, MouseEventArgs e)
        {
            HitTestResult result = ChartTab.HitTest(e.X, e.Y);
            
            //highligh selected series
            
            if (result.ChartElementType == ChartElementType.LegendItem)
            {
               Series current = new Series(); 
                foreach (Series srs in ChartTab.Series)
                {
                    if (srs.BorderWidth == 3)
                    {
                        current = srs;
                        break;
                    }
                }
                
                LegendItem item = (LegendItem)result.Object;
                current.BorderWidth = 1;
                if (item.Name != current.Name)
                    ChartTab.Series[item.SeriesName].BorderWidth = 3;

            }
            else // if any other area, make it selected chart
            {
                PerfChart area = (PerfChart)result.ChartArea;
                if (area != null)
                    this.SelectedPerfChart = area;
            }
        }
        protected void SetSelected(PerfChart chart)
        {
            if (_SelectedIndex != -1)
            {
                //unselect current
                PerfChart current = (PerfChart)ChartTab.ChartAreas[_SelectedIndex];
                current.BorderDashStyle = ChartDashStyle.NotSet;
                current.BorderWidth = 1;
            }
            //new selected
            chart.BorderDashStyle = ChartDashStyle.Solid;
            chart.BorderWidth = 3;
            
        }
        #endregion
        #region Properties
        public Chart ChartTab { get; private set; }
        public int SelectedIndex
        {
            get { return _SelectedIndex;}
            set { _SelectedIndex = value; }
        }
        public PerfChart SelectedPerfChart
        {
            get { return (PerfChart)ChartTab.ChartAreas[_SelectedIndex]; }
            set 
            {
                SetSelected(value);
                _SelectedIndex= ChartTab.ChartAreas.IndexOf(value.Name);
            }
        }
        public bool AutoSelectNewPerfChart { get; set; }
        public bool Started { get; private set; }
        #endregion
        #region Methods
        public void AddChart(PerfChart chart)
        {
            ChartTab.ChartAreas.Add(chart);
            if (AutoSelectNewPerfChart)
            {
                SelectedPerfChart = chart;
            }
        }
        public void AddCounterToSelected(Counter ctr)
        {
            string name = SelectedPerfChart.Name;
            Legend newleg;
            if (ChartTab.Legends.FindByName(name) == null)
            {
                newleg = new Legend(name);
                newleg.IsDockedInsideChartArea = false;
                newleg.Docking = Docking.Bottom;
                newleg.DockedToChartArea = name;
                //LegendCellColumn colname;
                ChartTab.Legends.Add(newleg);
            }
            string ctrlegname="";
            if (ctr.PerfCounter.MachineName != ".") { ctrlegname = ctr.PerfCounter.MachineName + " - "; }
            ctrlegname += ctr.PerfCounter.CounterName;
            if (ctr.PerfCounter.InstanceName != "") { ctrlegname += " - " + ctr.PerfCounter.InstanceName; }
            ctr.ChartArea = name;
            ctr.Legend = name;
            ctr.Name = ctrlegname;
            ChartTab.Series.Add(ctr);
            
        }
        public void Start()
        {
            if (!Started)
            {
                _timer.Start();
                Started = true;
            }
        }
        public void Stop()
        {
            if (Started)
            {
                _timer.Stop();
                Started = false;
            }
        }
        #endregion
    }
    class PerfChart : ChartArea
    {
        #region Member Fields
        #endregion
        #region Constructor
        /// <summary> 
        /// The default constructor 
        /// </summary> 
        public PerfChart()
            : base()
        {
            DateTime minvalue = DateTime.Now;
            DateTime maxvalue = minvalue.AddSeconds(120);

            this.AxisX.Minimum = minvalue.ToOADate();
            this.AxisX.Maximum = maxvalue.ToOADate();
            this.AxisX.LabelStyle.Format = "h:mm:ss tt";
            this.AxisX.MajorGrid.LineColor = System.Drawing.Color.LightGray;

            this.AxisY.Maximum = double.NaN;
            this.AxisY.MajorGrid.LineColor = System.Drawing.Color.LightGray;
        }
        #endregion
        #region PrivateMethods
        #endregion
        #region Properties
        #endregion
        #region Methods

        #endregion

    }
    class Counter : Series
    {
        #region Member Fields

        #endregion
        #region Constructor
        /// <summary> 
        /// The default constructor 
        /// </summary> 
        public Counter()
            :base()
        {
            this.XValueType = ChartValueType.Time;
            this.ChartType = SeriesChartType.Line;
        }
        public Counter(PerformanceCounter PerfCounter)
            : base()
        {
            this.PerfCounter = PerfCounter;
            this.XValueType = ChartValueType.Time;
            this.ChartType = SeriesChartType.Line;
        }
        #endregion
        #region PrivateMethods
        #endregion
        #region Properties
        public PerformanceCounter PerfCounter { get; set; }
        #endregion
        #region Methods

        #endregion

    }
}
